package com.tgy.studentmanagement.utils;

import com.tgy.studentmanagement.config.MinIoProperties;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.lf5.util.StreamUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;

import javax.validation.constraints.NotNull;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Comparator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/***
 * @ClassName: MinIoUtil
 * @Description:
 * @Auther: tanggy3
 * @Date: 9:58 2023/7/14
 * @version : V1.0
 */
@Slf4j
@Component
public class MinIoUtil {

    @Autowired
    MinIoProperties minIoProperties;

    private static MinioClient minioClient;


    File tempDir;
    File tempDirFinal;

    /**
     * 初始化minio配置
     *
     * @param :
     * @return: void
     * @date : 2020/8/16 20:56
     */
    @PostConstruct
    public void init() {
        try {
            minioClient = new MinioClient(minIoProperties.getUrl(), minIoProperties.getAccessKey(),
                    minIoProperties.getSecretKey());
            createBucket(minIoProperties.getBucketName());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("初始化minio配置异常: 【{}】", e.fillInStackTrace());
        }
    }

    /**
     * 判断 bucket是否存在
     *
     * @param bucketName: 桶名
     * @return: boolean
     * @date : 2020/8/16 20:53
     */
    @SneakyThrows(Exception.class)
    public static boolean bucketExists(String bucketName) {
        return minioClient.bucketExists(bucketName);
    }

    /**
     * 创建 bucket
     *
     * @param bucketName: 桶名
     * @return: void
     * @date : 2020/8/16 20:53
     */
    @SneakyThrows(Exception.class)
    public static void createBucket(String bucketName) {
        boolean isExist = minioClient.bucketExists(bucketName);
        if (!isExist) {
            minioClient.makeBucket(bucketName);
        }
    }

    /**
     * 获取全部bucket
     *
     * @param :
     * @return: java.util.List<io.minio.messages.Bucket>
     * @date : 2020/8/16 23:28
     */
    @SneakyThrows(Exception.class)
    public static List<Bucket> getAllBuckets() {
        return minioClient.listBuckets();
    }

    /**
     * 文件上传
     *
     * @param bucketName: 桶名
     * @param fileName:   文件名
     * @param filePath:   文件路径
     * @return: void
     * @date : 2020/8/16 20:53
     */
    @SneakyThrows(Exception.class)
    public static void upload(String bucketName, String fileName, String filePath) {
        minioClient.putObject(bucketName, fileName, filePath, null);
    }

    /**
     * 文件上传
     *
     * @param bucketName: 桶名
     * @param fileName:   文件名
     * @param stream:     文件流
     * @return: java.lang.String : 文件url地址
     * @date : 2020/8/16 23:40
     */
    @SneakyThrows(Exception.class)
    public static String upload(String bucketName, String fileName, InputStream stream) {
        minioClient.putObject(bucketName, fileName, stream, new PutObjectOptions(stream.available(), -1));
        return getFileUrl(bucketName, fileName);
    }

    /**
     * 文件上传
     *
     * @param bucketName: 桶名
     * @param file:       文件
     * @return: java.lang.String : 文件url地址
     * @date : 2020/8/16 23:40
     */
    @SneakyThrows(Exception.class)
    public static String upload(String bucketName, MultipartFile file) {
        final InputStream is = file.getInputStream();
        final String fileName = file.getOriginalFilename();
        minioClient.putObject(bucketName, fileName, is, new PutObjectOptions(is.available(), -1));
        is.close();
        return getFileUrl(bucketName, fileName);
    }

    /**
     * 删除文件
     *
     * @param bucketName: 桶名
     * @param fileName:   文件名
     * @return: void
     * @date : 2020/8/16 20:53
     */
    @SneakyThrows(Exception.class)
    public static void deleteFile(String bucketName, String fileName) {
        minioClient.removeObject(bucketName, fileName);
    }

    /**
     * 下载文件
     *
     * @param bucketName: 桶名
     * @param fileName:   文件名
     * @param response:
     * @return: void
     * @date : 2020/8/17 0:34
     */
    @SneakyThrows(Exception.class)
    public static void download(String bucketName, String fileName, HttpServletResponse response) {
        // 获取对象的元数据
        final ObjectStat stat = minioClient.statObject(bucketName, fileName);
        response.setContentType(stat.contentType());
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        InputStream is = minioClient.getObject(bucketName, fileName);
        IOUtils.copy(is, response.getOutputStream());
        is.close();
    }


    /**
     * 下载单个zip到浏览器
     * @param bucketName
     * @param fileNames
     * @param response
     */
    @SneakyThrows(Exception.class)
    public  void download(String bucketName, List<String> fileNames, HttpServletResponse response) {
        try {
            // 设置响应头，告诉浏览器返回的是zip文件
            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", "attachment; filename=\"files.zip\"");

            // 创建ZipOutputStream
            ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()));

            for (String fileName : fileNames) {
                InputStream download = minioClient.getObject(bucketName, fileName);
                // 获取文件的InputStream
                BufferedInputStream fileInputStream = new BufferedInputStream(download);

                // 创建ZipEntry，并设置文件名
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOut.putNextEntry(zipEntry);

                // 将文件内容写入ZipOutputStream
                StreamUtils.copy(fileInputStream, zipOut);

                // 关闭当前ZipEntry
                zipOut.closeEntry();

                // 关闭InputStream
                fileInputStream.close();
            }

            // 关闭ZipOutputStream
            zipOut.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取minio文件的下载地址
     *
     * @param bucketName: 桶名
     * @param fileName:   文件名
     * @return: java.lang.String
     * @date : 2020/8/16 22:07
     */
    @SneakyThrows(Exception.class)
    public static String getFileUrl(String bucketName, String fileName) {
        return minioClient.presignedGetObject(bucketName, fileName);
    }


    /**
     * 先创建多个单独的压缩包，再把多个单独的压缩包打包成一个大的压缩包
     * @param bucket
     * @param fileNames
     * @param response
     * @throws IOException
     */
    public void downloadFiles(@RequestParam("bucket") String bucket,
                              @RequestParam("fileNames") List<String> fileNames,
                              HttpServletResponse response) throws IOException {
        try {

            File tempDir = getFile(bucket, fileNames);

            // 创建最终的大压缩包
            File finalZipFile = new File(tempDirFinal,"final.zip");//这里名字要改
            createFinalZip(tempDir, finalZipFile);

            // 设置响应头，告诉浏览器返回的是zip文件
            response.setContentType("application/zip");
            response.setHeader("Content-Disposition", "attachment; filename=\"final.zip\"");

            // 将最终的大压缩包发送给客户端进行下载
            FileInputStream finalZipInputStream = new FileInputStream(finalZipFile);
            org.springframework.util.StreamUtils.copy(finalZipInputStream, response.getOutputStream());

            // 关闭InputStream
            finalZipInputStream.close();
            deleteTempDirectory(tempDir.toPath());
            deleteTempDirectory(tempDirFinal.toPath());
        } catch (Exception e) {
            // 处理MinIO异常
            e.printStackTrace();
        }
    }


    /**
     * 递归压缩成一个大压缩包
     *
     * @param file
     * @param zipOut
     * @throws IOException
     */
    private void recursiveZip(File file, ZipOutputStream zipOut) throws Exception {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File subFile : files) {
                    recursiveZip(subFile, zipOut);
                }
            }
        } else {
            FileInputStream fis = new FileInputStream(file);
            ZipEntry zipEntry = new ZipEntry(file.getName());
            zipOut.putNextEntry(zipEntry);

            long size = fis.getChannel().size();
            log.info("大小为{}",size);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                zipOut.write(buffer, 0, bytesRead);
                log.info("读取{}",bytesRead);
                //Thread.sleep(1000);
            }

            fis.close();
        }
    }


    //创建零时文件
    private void createTempDirectory() throws IOException {
        //String s = Long.toString(System.nanoTime());
        String s = "tgy";
        tempDir = File.createTempFile("temp", s);
        tempDir.delete();
        tempDir.mkdir();
        tempDirFinal = File.createTempFile("temp", s+"zip111");
        tempDirFinal.delete();
        tempDirFinal.mkdir();
        tempDirFinal.deleteOnExit();
    }


    /**
     * 得到存放多个文件的目录，这个目录就是一个子压缩包
     *
     * @param bucket
     * @param fileNames
     * @return
     * @throws Exception
     */
    @NotNull
    private File getFile(String bucket, List<String> fileNames) throws Exception {
        // 创建临时文件夹用于存储多个ZIP文件
        createTempDirectory();

        // 逐个压缩文件
        for (String fileName : fileNames) {
            // 获取文件的InputStream
            InputStream fileInputStream = minioClient.getObject(bucket, fileName);

            // 创建压缩包的临时文件
            File zipFile = new File(tempDir, fileName + ".zip");

            // 创建ZipOutputStream
            ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));

            // 创建ZipEntry，并设置文件名
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOut.putNextEntry(zipEntry);

            // 将文件内容写入ZipOutputStream
            org.springframework.util.StreamUtils.copy(fileInputStream, zipOut);

            // 关闭当前ZipEntry
            zipOut.closeEntry();

            // 关闭ZipOutputStream
            zipOut.close();

            // 关闭InputStream
            fileInputStream.close();
        }
        return tempDir;
    }

    /**
     * 创建最终的压缩文件
     *
     * @param tempDir
     * @param finalZipFile
     * @throws IOException
     */
    private void createFinalZip(File tempDir, File finalZipFile) throws Exception {
        FileOutputStream fos = new FileOutputStream(finalZipFile);
        ZipOutputStream zipOut = new ZipOutputStream(fos);

        // 递归压缩临时文件夹中的所有ZIP文件
        recursiveZip(tempDir, zipOut);

        // 关闭ZipOutputStream
        zipOut.close();

        // 关闭FileOutputStream
        fos.close();
    }


    /**
     * 删除临时目录
     * @param directory
     * @throws IOException
     */
    private void deleteTempDirectory(Path directory) throws IOException {
        if (Files.exists(directory)) {
            Files.walk(directory)
                    .sorted(Comparator.reverseOrder())
                    .map(Path::toFile)
                    .forEach(File::delete);
        }
    }

}

